package search

import (
	"bytes"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/PuerkitoBio/goquery"
	//"github.com/joho/godotenv"
)

/*/ 加载环境变量
func init() {
	if err := godotenv.Load(); err != nil {
		fmt.Fprintf(os.Stderr, "警告：加载.env文件失败: %v\n", err)
	}
}*/

// 全局变量
var (
	USER_AGENT = getEnv("USER_AGENT", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
	// 存储搜索结果，key为resultID
	searchResults = make(map[string]CnBingResult)
)

// CnBingResult 搜索结果结构体
type CnBingResult struct {
	ID      string `json:"id"`
	Title   string `json:"title"`
	Link    string `json:"link"`
	Snippet string `json:"snippet"`
}

// getEnv 获取环境变量，不存在则返回默认值
func getEnv(key, defaultValue string) string {
	if value, exists := os.LookupEnv(key); exists {
		return value
	}
	return defaultValue
}

// generateID 生成唯一结果ID
func generateID(prefix string) string {
	b := make([]byte, 8)
	rand.Read(b)
	randomStr := base64.URLEncoding.EncodeToString(b)[:12]
	return fmt.Sprintf("%s_%d_%s", prefix, time.Now().UnixMilli(), randomStr)
}

// fixURL 修复不完整的URL
func fixURL(rawURL string) string {
	if strings.HasPrefix(rawURL, "http") {
		return rawURL
	}
	baseURL := "https://cn.bing.com"
	if strings.HasPrefix(rawURL, "/") {
		return baseURL + rawURL
	}
	return fmt.Sprintf("%s/%s", baseURL, rawURL)
}

// searchBing 必应搜索函数
func SearchCnBing(query string, numResults int) ([]CnBingResult, error) {
	// 构建搜索URL
	encodedQuery := url.QueryEscape(query)
	searchURL := fmt.Sprintf("https://cn.bing.com/search?q=%s&setlang=zh-CN&ensearch=0", encodedQuery)
	fmt.Fprintf(os.Stderr, "正在搜索URL: %s\n", searchURL)

	// 创建请求并设置头部
	req, err := http.NewRequest("GET", searchURL, nil)
	if err != nil {
		return nil, fmt.Errorf("创建请求失败: %v", err)
	}

	headers := map[string]string{
		"User-Agent":                USER_AGENT,
		"Accept":                    "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
		"Accept-Language":           "zh-CN,zh;q=0.9,en;q=0.8",
		"Cache-Control":             "no-cache",
		"Pragma":                    "no-cache",
		"Sec-Fetch-Dest":            "document",
		"Sec-Fetch-Mode":            "navigate",
		"Sec-Fetch-Site":            "none",
		"Sec-Fetch-User":            "?1",
		"Upgrade-Insecure-Requests": "1",
		"Cookie":                    "SRCHHPGUSR=SRCHLANG=zh-Hans; _EDGE_S=ui=zh-cn; _EDGE_V=1",
	}

	for k, v := range headers {
		req.Header.Set(k, v)
	}

	// 发送请求（设置15秒超时）
	client := &http.Client{Timeout: 15 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("发送请求失败: %v", err)
	}
	defer resp.Body.Close()

	fmt.Fprintf(os.Stderr, "搜索响应状态: %d\n", resp.StatusCode)

	// 读取响应内容
	bodyBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("读取响应失败: %v", err)
	}
	fmt.Fprintf(os.Stderr, "响应内容长度: %d 字节\n", len(bodyBytes))
	snippetSize := 200
	if len(bodyBytes) > snippetSize {
		fmt.Fprintf(os.Stderr, "响应内容前 %d 字符: %s\n", snippetSize, string(bodyBytes[:snippetSize]))
	}

	// 使用goquery解析HTML
	doc, err := goquery.NewDocumentFromReader(bytes.NewReader(bodyBytes))
	if err != nil {
		return nil, fmt.Errorf("解析HTML失败: %v", err)
	}

	var results []CnBingResult

	// 调试：打印选择器匹配数量
	checkSelectorCount := func(selector string) {
		count := doc.Find(selector).Length()
		fmt.Fprintf(os.Stderr, "选择器 %s 匹配了 %d 个元素\n", selector, count)
	}

	checkSelectorCount("#b_results > li")
	checkSelectorCount("#b_results")
	checkSelectorCount(".b_algo")
	checkSelectorCount(".b_title")
	checkSelectorCount(".b_caption")

	// 尝试不同的结果选择器
	resultSelectors := []string{
		"#b_results > li.b_algo",
		"#b_results > .b_ans",
		"#b_results > li",
	}

	found := false
	for _, selector := range resultSelectors {
		fmt.Fprintf(os.Stderr, "尝试选择器: %s\n", selector)
		doc.Find(selector).Each(func(index int, s *goquery.Selection) {
			if len(results) >= numResults {
				return
			}

			// 调试：打印元素HTML片段
			html, _ := s.Html()
			if len(html) > 100 {
				html = html[:100]
			}
			fmt.Fprintf(os.Stderr, "元素 %d HTML 片段: %s\n", index, html)

			// 提取标题和链接
			title := ""
			link := ""
			titleElement := s.Find("h2 a").First()
			if titleElement.Length() > 0 {
				title = strings.TrimSpace(titleElement.Text())
				link, _ = titleElement.Attr("href")
			}

			// 备用标题选择器
			if title == "" {
				altTitleElements := []string{".b_title a", "a.tilk", "a strong"}
				for _, altSel := range altTitleElements {
					altEl := s.Find(altSel).First()
					if altEl.Length() > 0 {
						title = strings.TrimSpace(altEl.Text())
						link, _ = altEl.Attr("href")
						break
					}
				}
			}

			// 提取摘要
			snippet := ""
			snippetSelectors := []string{".b_caption p", ".b_snippet", ".b_algoSlug"}
			for _, snipSel := range snippetSelectors {
				snipEl := s.Find(snipSel).First()
				if snipEl.Length() > 0 {
					snippet = strings.TrimSpace(snipEl.Text())
					break
				}
			}

			// 备用摘要提取
			if snippet == "" {
				fullText := strings.TrimSpace(s.Text())
				// 移除标题
				if title != "" {
					fullText = strings.ReplaceAll(fullText, title, "")
					fullText = strings.TrimSpace(fullText)
				}
				// 截取长度
				if len(fullText) > 150 {
					fullText = fullText[:150] + "..."
				}
				snippet = fullText
			}

			// 修复链接
			link = fixURL(link)

			// 跳过广告和空结果
			if (title == "" && snippet == "") || s.HasClass("b_ad") {
				return
			}

			// 创建结果并存储
			resultID := generateID("result")
			result := CnBingResult{
				ID:      resultID,
				Title:   title,
				Link:    link,
				Snippet: snippet,
			}

			fmt.Fprintf(os.Stderr, "找到结果 %d: 标题=\"%s\", 链接=\"%s...\"\n",
				index, title, truncateString(link, 30))

			searchResults[resultID] = result
			results = append(results, result)
		})

		if len(results) > 0 {
			fmt.Fprintf(os.Stderr, "使用选择器 %s 找到了 %d 个结果，停止继续搜索\n", selector, len(results))
			found = true
			break
		}
	}

	// 如果未找到结果，尝试提取所有链接
	if !found {
		fmt.Fprintf(os.Stderr, "使用选择器未找到结果，尝试直接提取链接\n")
		doc.Find("a").Each(func(index int, s *goquery.Selection) {
			if len(results) >= numResults {
				return
			}

			title := strings.TrimSpace(s.Text())
			link, _ := s.Attr("href")

			// 跳过无效链接
			if title == "" || link == "" || link == "#" || strings.HasPrefix(link, "javascript:") {
				return
			}

			// 修复链接
			link = fixURL(link)

			// 检查是否包含关键词
			lowerQuery := strings.ToLower(query)
			lowerTitle := strings.ToLower(title)
			lowerLink := strings.ToLower(link)
			isLikelyResult := strings.Contains(lowerLink, "bing.com/search") ||
				strings.Contains(lowerTitle, lowerQuery) ||
				strings.Contains(lowerLink, lowerQuery)

			if isLikelyResult {
				resultID := generateID("result")
				snippet := fmt.Sprintf("来自 %s 的结果", link)
				result := CnBingResult{
					ID:      resultID,
					Title:   title,
					Link:    link,
					Snippet: snippet,
				}

				fmt.Fprintf(os.Stderr, "提取到可能的结果链接: %s - %s\n", title, link)
				searchResults[resultID] = result
				results = append(results, result)
			}
		})
	}

	// 最终降级方案：返回原始搜索链接
	if len(results) == 0 {
		fmt.Fprintf(os.Stderr, "未找到任何结果，添加原始搜索链接作为结果\n")
		resultID := generateID("result")
		result := CnBingResult{
			ID:      resultID,
			Title:   fmt.Sprintf("搜索结果: %s", query),
			Link:    searchURL,
			Snippet: fmt.Sprintf("未能解析关于 \"%s\" 的搜索结果，但您可以直接访问必应搜索页面查看。", query),
		}
		searchResults[resultID] = result
		results = append(results, result)
	}

	fmt.Fprintf(os.Stderr, "最终返回 %d 个结果\n", len(results))
	return results, nil
}

// fetchWebpageContent 根据结果ID获取网页内容
func fetchWebpageContent(resultID string) (string, error) {
	// 获取结果信息
	result, exists := searchResults[resultID]
	if !exists {
		return "", fmt.Errorf("找不到ID为 %s 的搜索结果", resultID)
	}

	urlStr := result.Link
	fmt.Fprintf(os.Stderr, "正在获取网页内容: %s\n", urlStr)

	// 创建请求并设置头部
	req, err := http.NewRequest("GET", urlStr, nil)
	if err != nil {
		return "", fmt.Errorf("创建请求失败: %v", err)
	}

	headers := map[string]string{
		"User-Agent":      USER_AGENT,
		"Accept":          "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
		"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
		"Cache-Control":   "no-cache",
		"Pragma":          "no-cache",
		"Referer":         "https://cn.bing.com/",
	}

	for k, v := range headers {
		req.Header.Set(k, v)
	}

	// 发送请求
	client := &http.Client{Timeout: 15 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return "", fmt.Errorf("发送请求失败: %v", err)
	}
	defer resp.Body.Close()

	fmt.Fprintf(os.Stderr, "获取网页响应状态: %d\n", resp.StatusCode)

	// 读取响应内容（处理编码）
	bodyBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("读取响应失败: %v", err)
	}

	// 解析HTML
	doc, err := goquery.NewDocumentFromReader(bytes.NewReader(bodyBytes))
	if err != nil {
		return "", fmt.Errorf("解析HTML失败: %v", err)
	}

	// 移除不需要的元素
	removeSelectors := []string{
		"script", "style", "iframe", "noscript", "nav", "header", "footer",
		".header", ".footer", ".nav", ".sidebar", ".ad", ".advertisement",
		"#header", "#footer", "#nav", "#sidebar",
	}
	for _, sel := range removeSelectors {
		doc.Find(sel).Remove()
	}

	// 提取主要内容
	var content string
	mainSelectors := []string{
		"main", "article", ".article", ".post", ".content", "#content",
		".main", "#main", ".body", "#body", ".entry", ".entry-content",
		".post-content", ".article-content", ".text", ".detail",
	}

	// 尝试主要内容选择器
	for _, sel := range mainSelectors {
		mainEl := doc.Find(sel).First()
		if mainEl.Length() > 0 {
			content = strings.TrimSpace(mainEl.Text())
			fmt.Fprintf(os.Stderr, "使用选择器 \"%s\" 找到内容，长度: %d 字符\n", sel, len(content))
			break
		}
	}

	// 尝试提取段落
	if content == "" || len(content) < 100 {
		fmt.Fprintf(os.Stderr, "未找到主要内容区域，尝试提取所有段落\n")
		var paragraphs []string
		doc.Find("p").Each(func(_ int, s *goquery.Selection) {
			text := strings.TrimSpace(s.Text())
			if len(text) > 20 { // 只保留有效段落
				paragraphs = append(paragraphs, text)
			}
		})

		if len(paragraphs) > 0 {
			content = strings.Join(paragraphs, "\n\n")
			fmt.Fprintf(os.Stderr, "从段落中提取到内容，长度: %d 字符\n", len(content))
		}
	}

	// 最后尝试body内容
	if content == "" || len(content) < 100 {
		fmt.Fprintf(os.Stderr, "从段落中未找到足够内容，获取body内容\n")
		content = strings.TrimSpace(doc.Find("body").Text())
	}

	// 清理文本
	spaceRegex := regexp.MustCompile(`\s+`)
	content = spaceRegex.ReplaceAllString(content, " ")
	content = strings.ReplaceAll(content, "\n\n\n", "\n\n")
	content = strings.TrimSpace(content)

	// 添加标题
	title := strings.TrimSpace(doc.Find("title").Text())
	if title != "" {
		content = fmt.Sprintf("标题: %s\n\n%s", title, content)
	}

	// 截断过长内容
	maxLength := 8000
	if len(content) > maxLength {
		content = content[:maxLength] + "... (内容已截断)"
	}

	fmt.Fprintf(os.Stderr, "最终提取内容长度: %d 字符\n", len(content))
	return content, nil
}

// truncateString 截断字符串到指定长度
func truncateString(s string, length int) string {
	if len(s) <= length {
		return s
	}
	return s[:length]
}
